Micron Document
🎖️GitЯра🎖️


Commit e0bc2c96dff0a6a8174bfbd3e22809fca3bf6daf


Parents : 2c4117c
Author : drkhsh <me@drkhsh.at>
Date : 2026-01-30T19:42:18+01:00

use Resource API for reliable data transfer

Changes

2 files changed, 41 insertions(+), 13 deletions(-)


Diff

diff --git a/gopher_rns/client.py b/gopher_rns/client.py
index 0b412c1..2241797 100644
--- a/gopher_rns/client.py
+++ b/gopher_rns/client.py
@@ -19,11 +19,13 @@ class GopherClient:
self._lock = threading.Lock()
self._response_data = b''
self._response_complete = False
+ self._resource_failed = False
def fetch(self, dest_hash, selector="", query=None, timeout=30):
with self._lock:
self._response_data = b''
self._response_complete = False
+ self._resource_failed = False
if isinstance(dest_hash, str):
dest_hash = bytes.fromhex(dest_hash.replace('<', '').replace('>', ''))
@@ -55,6 +57,8 @@ class GopherClient:
if link.status != RNS.Link.ACTIVE:
raise ConnectionError("Link establishment timeout")
+ link.set_resource_strategy(RNS.Link.ACCEPT_ALL)
+ link.set_resource_concluded_callback(self._resource_concluded)
link.set_packet_callback(self._packet_callback)
request = f"{selector}\t{query}" if query else selector
@@ -63,15 +67,19 @@ class GopherClient:
start = time.time()
while True:
with self._lock:
- if self._response_complete or time.time() - start >= timeout:
+ if self._response_complete or self._resource_failed:
+ break
+ if time.time() - start >= timeout:
break
time.sleep(0.1)
link.teardown()
with self._lock:
- complete, data = self._response_complete, self._response_data
+ complete, failed, data = self._response_complete, self._resource_failed, self._response_data
+ if failed:
+ raise ConnectionError("Resource transfer failed")
if not complete:
raise TimeoutError("Response timeout")
return data
@@ -80,8 +88,14 @@ class GopherClient:
with self._lock:
if len(data) == 0:
self._response_complete = True
+
+ def _resource_concluded(self, resource):
+ with self._lock:
+ if resource.status == RNS.Resource.COMPLETE:
+ self._response_data = resource.data.read()
+ self._response_complete = True
else:
- self._response_data += data
+ self._resource_failed = True
class HistoryEntry:

diff --git a/gopher_rns/server.py b/gopher_rns/server.py
index c3fe4c7..65b3438 100644
--- a/gopher_rns/server.py
+++ b/gopher_rns/server.py
@@ -1,5 +1,6 @@
import os
import subprocess
+import threading
import time
import RNS
@@ -37,6 +38,9 @@ class GopherServer:
"server"
)
+ self._links = {}
+ self._lock = threading.Lock()
+
self.destination.set_link_established_callback(self._link_established)
def start(self):
@@ -52,6 +56,12 @@ class GopherServer:
def _link_established(self, link):
RNS.log(f"Link established from {RNS.prettyhexrep(link.destination.hash)}")
link.set_packet_callback(lambda data, packet: self._handle_request(data, link))
+ link.set_link_closed_callback(self._link_closed)
+
+ def _link_closed(self, link):
+ with self._lock:
+ if link in self._links:
+ del self._links[link]
def _handle_request(self, data, link):
selector, query = protocol.parse_selector(data)
@@ -66,16 +76,20 @@ class GopherServer:
self._send_response(link, error_response)
def _send_response(self, link, data):
- chunk_size = RNS.Link.MDU
- offset = 0
-
- while offset < len(data):
- chunk = data[offset:offset + chunk_size]
- packet = RNS.Packet(link, chunk)
- packet.send()
- offset += chunk_size
-
- RNS.Packet(link, b'').send()
+ if len(data) == 0:
+ RNS.Packet(link, b'').send()
+ return
+
+ def resource_concluded(resource):
+ RNS.log(f"Resource transfer completed: {resource.status}")
+ with self._lock:
+ if link in self._links:
+ del self._links[link]
+
+ resource = RNS.Resource(data, link, callback=resource_concluded)
+ with self._lock:
+ self._links[link] = resource
+ RNS.log(f"Started resource transfer: {len(data)} bytes")
def _serve(self, selector, query):
path = utils.safe_path(self.content_dir, selector)

Served by rngit 1.5.2 - Generated in 0.06s